package com.unitd.frame.sso.common.helper;

import com.unitd.frame.comm.utils.RandomUtil;
import com.unitd.frame.sso.common.config.SSOConfig;
import com.unitd.frame.sso.common.token.AuthToken;
import com.unitd.frame.sso.common.token.Token;
import com.unitd.frame.sso.service.sso.ISSOService;
import com.unitd.frame.sso.service.sso.impl.SSOServiceImpl;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @desc 外围系统调用SSO的基础工具类, 基本上所有的需要使用的SSO属性、方法, 都可以通过此类中的方法来进行调用
 * @filename SSOHelper.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2016/10/14
 */
public class SSOHelper {

	private static ISSOService ssoService = null;

	/**
	 * @desc 初始化SSO服务
	 * @return sso服务
	 */
	public static ISSOService getSSOService() {
		if (ssoService == null) {
			ssoService = new SSOServiceImpl();
		}
		return ssoService;
	}

	/* ************************** 登录/登出/统计相关处理方法 ************************** */
	/**
	 * @desc 当用户验证登录成功后, 设置当前登录用户的加密 Cookie
	 * 最后一个参数为 true,表示销毁当前 JSESSIONID 并重新创建可信的 JSESSIONID 防止伪造 SESSIONID 攻击
	 * 最后一个参数为 false,表示只设置 cookie,JSESSIONID
	 * request.setAttribute(SSOConfig.SSO_COOKIE_MAXAGE, maxAge);
	 * 可以动态设置 Cookie maxAge 超时时间 ,优先于配置文件的设置,无该参数 - 默认读取配置文件数据
	 * maxAge 定义:-1 浏览器关闭时自动删除; 0 立即删除; 120 表示Cookie有效期2分钟(以秒为单位)
	 * @param request    http请求对象
	 * @param response   http相应对象
	 * @param token      SSO登录票据对象
	 * @param invalidate 是否销毁当前 jsessionid
	 */
	public static void setSSOCookie(HttpServletRequest request, HttpServletResponse response, Token token, boolean invalidate) {
		if (invalidate) {
			getSSOService().authSSOCookie(request, response, token);
		} else {
			getSSOService().setSSOCookie(request, response, token);
		}
	}

	/**
	 * @desc 统计当前通过SSO登录的登录人数(合计多少个)
	 * @param request http请求对象
	 * @return 当前登录人数
	 */
	public static String getLoginCount(HttpServletRequest request) {
		return getSSOService().getLoginCount(request);
	}

	/**
	 * @desc 退出登录, 并且跳至 sso.properties 配置文件中的属性 sso.logout.url 指定的登出URL地址
	 * (只是简单的退出当前登录, 不对Cookie、缓存、统计、等数据进行处理)
	 * @param request  http请求对象
	 * @param response http相应对象
	 * @throws IOException
	 */
	public static void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {
		getSSOService().logout(request, response);
	}

	/**
	 * @desc 根据指定的用户 ID 踢出该登录用户,退出当前系统
	 * @param userId 用户ID
	 * @return boolean 踢出登录成功与否
	 */
	public static boolean kickLogin(Object userId) {
		return getSSOService().kickLogin(userId);
	}

	/**
	 * @desc 退出登录并清理当前登录状态;清理 Cookie、缓存、统计、等数据,但不进行重定向
	 * @param request  http请求对象
	 * @param response http相应对象
	 * @return
	 */
	public static boolean clearLogin(HttpServletRequest request, HttpServletResponse response) {
		return getSSOService().clearLogin(request, response);
	}

	/**
	 * @desc 退出登录并清理当前登录状态;清理 Cookie、缓存、统计、等数据;
	 * 完成后,重定向到sso.properties 配置文件中的属性 sso.login.url 指定的登录URL地址
	 * 一般情况下,使用此方法作为登出处理
	 * @param request  http请求对象
	 * @param response http相应对象
	 * @throws IOException
	 */
	public static void clearRedirectLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
		getSSOService().clearRedirectLogin(request, response);
	}

	/* ************************** 客户端处理Token相关方法 ************************** */
	/**
	 * @desc 获取当前请求 Token
	 * 该方法直接从 cookie 中解密获取 token, 常在登录系统及拦截器中使用 getToken(request)
	 * 如果该请求在登录拦截器之后请使用 attrToken(request) 防止二次解密
	 * @param request http请求对象
	 * @param <T>     请求Token类型
	 * @return 当前请求的具体Token
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Token> T getToken(HttpServletRequest request) {
		return (T) getSSOService().getToken(request);
	}

	/**
	 * @desc 从请求中获取 token 通过登录拦截器之后使用
	 * 该数据为登录拦截器放入 request 中的数据,这里直接获取即可,防止二次解密
	 * @param request 访问请求
	 * @return 当前请求的具体Token
	 */
	public static <T extends Token> T attrToken(HttpServletRequest request) {
		return getSSOService().attrToken(request);
	}

	/**
	 * @desc 获取当前请求中的 token 的缓存主键
	 * @param request 当前请求
	 * @return token 的缓存主键
	 */
	public static String getTokenCacheKey(HttpServletRequest request) {
		return getToken(request).toCacheKey();
	}

	/**
	 * @desc 根据当前登录的请求用户的ID, 获取 token 的缓存主键
	 * @param userId 用户ID
	 * @return token 的缓存主键
	 */
	public static String getTokenCacheKey(Object userId) {
		return SSOConfig.toCacheKey(userId);
	}

	/**
	 * @desc 生成18位的随机字符串密钥, 用于替换配置文件sso.properties中的属性【sso.secretkey=随机18位字符串】
	 * @return 18位的随机字符串密钥
	 */
	public static String getSecretKey() {
		return RandomUtil.getCharacterAndNumber(18);
	}

	/* ************************** 跨域处理相关方法 ************************** */
	/**
	 * @desc 根据RSA私钥生成对应的跨域询问密文(公钥, 用于跨域处理)
	 * @param request    http请求对象
	 * @param response   http相应对象
	 * @param privateKey RSA 私钥(业务系统私钥,用于签名)
	 * @return AuthToken {@link AuthToken}
	 */
	public static AuthToken askCiphertext(HttpServletRequest request, HttpServletResponse response, String privateKey) {
		return getSSOService().askCiphertext(request, response, privateKey);
	}

	/**
	 * @desc 根据请求的询问密文生成对应的跨域回复密文(公钥,用于跨域处理)
	 * @param askData 询问密文
	 * @return AuthToken {@link AuthToken}
	 */
	public static AuthToken replyCiphertext(HttpServletRequest request, String askData) {
		return getSSOService().replyCiphertext(request, askData);
	}

	/**
	 * @desc 验证跨域回复密文的有效性, 如果成功则返回绑定的用户ID 等信息
	 * @param request   http请求对象
	 * @param response  http相应对象
	 * @param replyData 回复密文
	 * @param ssoClientPrk  RSA 公钥 (业务系统公钥，验证authToken签名, SSO客户端自有的公钥)
	 * @param ssoServerPro  RSA 公钥 (SSO 回复密文公钥验证签名, SSO服务端对应发布的公钥)
	 * @return AuthToken {@link AuthToken}
	 */
	public static AuthToken ok(HttpServletRequest request, HttpServletResponse response, String replyData, String ssoClientPrk,
							   String ssoServerPro) {
		return getSSOService().ok(request, response, replyData, ssoClientPrk, ssoServerPro);
	}
}